package JET.geom;

/**
 *
 * @author 057Ry
 */
public final class Vec3d
{
    public double x,y,z;
    
    public Vec3d()
    {
        x = 0;
        y = 0;
        z = 0;
    }
    public Vec3d( double x, double y, double z )
    {
        this.x = x;
        this.y = y;
        this.z = z;
    }
    public Vec3d( Vec3d vec )
    {
        this.x = vec.x;
        this.y = vec.y;
        this.z = vec.z;
    }

    public double getX() {
        return x;
    }

    public double getY() {
        return y;
    }

    public double getZ() {
        return z;
    }
    
    public void become( Vec3d vec )
    {
        this.x = vec.x;
        this.y = vec.y;
        this.z = vec.z;
    }
         
    public float getMagnitudef()
    {
        return (float)Math.sqrt( x*x + y*y + z*z );
    }
    
    public double getMagnitude()
    {
        return Math.sqrt( x*x + y*y + z*z );
    }
    
    public double getScalarProduct( Vec3d arg )
    {
        return x*arg.x + y*arg.y + z*arg.z;
    }
    public Vec3d getCrossProduct( Vec3d b )
    {
        return new Vec3d( this.y*b.z - this.z*b.y,
                          this.z*b.x - this.x*b.z,
                          this.x*b.y - this.y*b.x );
    }
    
    public double getAngleX()
    {
        Vec3d tmp = new Vec3d( 1,0,0 );
        double cosAngle = getScalarProduct( tmp )/getMagnitudef();
        return Math.acos( cosAngle );
    }
    public double getAngleY()
    {
        Vec3d ny = new Vec3d(0,1,0); // ny - normal Y
        double cosAngle = getScalarProduct( ny )/getMagnitudef();
        return Math.acos( cosAngle );
    }
    public double getAngleZ()
    {
        Vec3d tmp = new Vec3d(0,0,1);
        double cosAngle = getScalarProduct( tmp )/getMagnitudef();
        return Math.acos( cosAngle );
    }
    public double getHorizAngle()
    {
        if ( x==0 && z==0 && y!=0 )
        {
            return 0;
        }
        else
        {
            Vec3d nX = new Vec3d( 1,0,0 ); // nx - normal X
            Vec3d nZ = new Vec3d( 0,0,1 ); // nz - normal Z
            

            double sX = getScalarProduct( nX );
            double sZ = getScalarProduct( nZ );
            
            Vec3d dX = new Vec3d(sX,0,0);
            Vec3d dZ = new Vec3d(0,0,sZ);
            
            Vec3d dXZ = new Vec3d();
            dXZ.add(dX);
            dXZ.add(dZ);
            dXZ.normalize();
            
            double horizAngle = Math.acos( nZ.getScalarProduct(dXZ) );
            if( dX.x<0 )
                return Math.PI*2f - horizAngle;
            else
                return horizAngle;
        }
    }
    public double getVerticAngle()
    {
        return getAngleY();
    }
    
    public void scale( double k )
    {
        x *= k;
        y *= k;
        z *= k;
    }
    public Vec3d getScaled( double k )
    {
        Vec3d tmp = new Vec3d();
        tmp.add( this );
        tmp.scale( k );
        return tmp;
    }
    public void normalize()
    {
        scale( 1.0d/getMagnitude() );
    }
    public Vec3d getNormalized()
    {
        Vec3d tmp = new Vec3d();
        tmp.add( this );
        tmp.normalize();
        return tmp;
    }
    public void rotateX(double a)
    {
        double nz = (z* Math.cos(a) - y*Math.sin(a));
        double ny = (z* Math.sin(a) + y*Math.cos(a));
        z = nz;
        y = ny;
    }
    public void rotateY(double a)
    {
        double nx = ( x*Math.cos(a) - z*Math.sin(a) );
        double nz = ( x*Math.sin(a) + z*Math.cos(a) );
        x = nx;
        z = nz;
    }
    public void rotateZ(double a)
    {
        double nx = ( x*Math.cos(a) - y*Math.sin(a) );
        double ny = ( x*Math.sin(a) + y*Math.cos(a) );
        x = nx;
        y = ny;
    }
    public void rotateAV(Vec3d axis, double a)
    {
        double hA = axis.getHorizAngle();
        double vA = axis.getVerticAngle();
        
        this.rotateY( -hA );
        // teraz nasz wektor jest w plaszczynie YZ
        this.rotateX( -vA );
        // teraz nasz wektor jest w kierunku osi Y
        
        this.rotateY( a );
        
        this.rotateX( vA );
        this.rotateY( hA );
    }
    public void add(Vec3d vec)
    {
        this.x += vec.x;
        this.y += vec.y;
        this.z += vec.z;
    }
    public Vec3d getAdded(Vec3d vec)
    {
        Vec3d newV = new Vec3d(this);
        newV.add(vec);

        return newV;
    }
    public void sub(Vec3d vec)
    {
        this.x -= vec.x;
        this.y -= vec.y;
        this.z -= vec.z;
    }
    public Vec3d getSubbed(Vec3d vec)
    {
        Vec3d newV = new Vec3d(this);
        newV.sub(vec);

        return newV;
    }
    @Override
    public String toString()
    {
        return "["+x+", "+y+", "+z+"]";
    }
    @Override
    public Vec3d clone()
    {
        return new Vec3d( x, y, z);
    }
}
